package sq

import (
	"database/sql"
	"errors"
	"gitee.com/gopher2011/sqlx"
	"log"
	"strings"
	"time"
)

//Config 是数据库连接配置。
type Config struct {
	Driver       string `toml:"driver" json:"driver"`
	DSN          string `toml:"dsn" json:"dsn"`
	MaxOpenConns int    `toml:"max_open_conns" json:"max_open_conns"`
	MaxIdleConns int    `toml:"max_idle_conns" json:"max_idle_conns"`
	MaxLifetime  int    `toml:"max_lefttime" json:"max_lefttime"`
	Enable       bool   `toml:"enable" json:"enable"`
	ShowSql      bool   `toml:"show_sql" json:"show_sql"` // 是否显示SQL语句，ture:显示，false:不显示。
}


var (
	defaultLink = "default" //defaultLink 设置数据库默认链接名称
	FatalExit = true //如果该值为true,则当前数据库退出。
	dbMap = make(map[string]*sqlx.DB, 0) // 管理 *sqlx.DB 的map容器。
)

// GetDB 获取指定的数据库引擎；如果未指定名称，则获取默认数据库。
func GetDB(name ...string) *sqlx.DB {
	dbName := defaultLink
	if name != nil {
		dbName = name[0]
	}
	engine, ok := dbMap[dbName]
	if !ok {
		log.Fatalf("[db] the database link `%s` is not configured", dbName)
	}
	return engine
}

// List 获取数据库引擎列表。
func List() map[string]*sqlx.DB {
	return dbMap
}

// NewDB 通过*sql.DB，返回一个 *sq.DB 对象。
func NewDB(driver string, db *sql.DB) *DB {
	return &DB{database: sqlx.NewDB(db, driver)}
}


// Open 打开一个数据库连接。返回一个 *sq.DB。
//  注意: 不要再显性的调用Ping()了，该函数默认已经 Ping()了。
func Open(driver, dbSource string) (*DB, error) {
	db, err := sqlx.Connect(driver, dbSource)

	if err != nil {
		return nil, err
	}
	return &DB{database: db}, nil
}

// Connect 连接数据库。
//  注意: 不要再显性的调用Ping()了，该函数默认已经 Ping()了。
func Connect (configs map[string]*Config) (err error) {
	var errs []string
	defer func() {
		if len(errs) > 0 {
			err = errors.New("[db] " + strings.Join(errs, "\n"))
			if FatalExit {
				log.Fatal(err)
			}
		}
	}()

	for key, conf := range configs {
		if !conf.Enable {
			continue
		}

		db, err2 := sqlx.Connect(conf.Driver, conf.DSN)

		if err2 != nil {
			errs = append(errs, err.Error())
			continue
		}
		log.Println("[db] connect:" + key)

		if conf.ShowSql {
			logger.SetLogging(true)
		}

		db.SetMaxOpenConns(conf.MaxOpenConns)
		db.SetMaxIdleConns(conf.MaxIdleConns)
		if conf.MaxLifetime > 0 {
			db.SetConnMaxLifetime(time.Duration(conf.MaxLifetime) * time.Second)
		}

		if d, ok := dbMap[key]; ok {
			_ = d.Close()
		}

		dbMap[key] = db
	}
	return
}
